package com.rockbb.common.simpleframe.config;

import java.io.InputStream;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;

import org.apache.log4j.Logger;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.InputSource;

import com.rockbb.common.simpleframe.action.ActionContext;
import com.rockbb.common.simpleframe.action.ActionNavigator;
import com.rockbb.common.simpleframe.result.ActionResult;
import com.rockbb.common.sso.acl.ACLBuilder;
import com.rockbb.common.utilities.i18n.Language;

public class SimpleConfig {

	private static Logger logger = Logger.getLogger(SimpleConfig.class);

	// 静态配置变量
	private static final Map<String, String> configs;

	// Action 列表
	private static final List<String> actions;

	// Action 配置集
	private static final Map<String, ActionContext> contexts;

	// 模板列表
	private static final List<String> styles;
	private static int acp_style_index;

	// 语言列表, 其index用于指代相應語言
	private static final List<Language> languages;

	/* 
	 * 角色与权限: 
	 * roles存放角色的名称列表, 
	 * rolesMap存放每个角色对应的ACL位置, 
	 * superusers存放可以行使超級用戶權限的ACL位置
	 */
	private static final List<String> roles;
	private static final Map<String, Integer> rolesMap;
	private static final List<Integer> superusers;

	// 页首导航条目
	private static final Map<String, ActionNavigator> navigations;

	static 
	{
		configs			= new HashMap<String, String>();
		actions			= new ArrayList<String>();
		contexts		= new HashMap<String, ActionContext>();
		styles			= new ArrayList<String>();
		languages		= new ArrayList<Language>();

		roles				= new ArrayList<String>();
		rolesMap		= new HashMap<String, Integer>();
		superusers	= new ArrayList<Integer>();

		navigations	= new HashMap<String, ActionNavigator>();

		try
		{
			InputStream is = SimpleConfig.class.getResourceAsStream("/simpleframe-config.xml");
			DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
			DocumentBuilder db = dbf.newDocumentBuilder();
			InputSource isc = new InputSource(is);
			Document doc = db.parse(isc);
			
			// 初始化静态配置变量
			NodeList nodeList = doc.getElementsByTagName("constant");
			for(int i=0; i<nodeList.getLength() ; i++)
			{
				Node node = nodeList.item(i);
				if (node.getNodeType() == Node.ELEMENT_NODE) 
				{
					Element element = (Element)node;
					configs.put(
						element.getAttribute("name"), 
						element.getAttribute("value")
					);
				}
			}

		}
		catch(Exception e)
		{
			logger.info("Error occurs in parsing Simpleframe configuration file");
			logger.debug(e.getMessage(), e);
		}
		
		try 
		{
			InputStream is = SimpleConfig.class.getResourceAsStream("/simpleframe-context.xml");
			DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
			DocumentBuilder db = dbf.newDocumentBuilder();
			InputSource isc = new InputSource(is);
			Document doc = db.parse(isc);

			// 初始化全局result集合
			NodeList nodeList = doc.getElementsByTagName("global-result");
			List<ActionResult> global_results = new ArrayList<ActionResult>();
			for(int i=0; i<nodeList.getLength() ; i++)
			{
				Node node = nodeList.item(i);
				if (node.getNodeType() == Node.ELEMENT_NODE) 
				{
					Element element = (Element)node;
					ActionResult result = new ActionResult();
					result.setName(element.getAttribute("name"));
					result.setType(Integer.parseInt(element.getAttribute("type")));
					result.setValue(element.getAttribute("value"));
					global_results.add(result);
				}
			}

			// 初始化 Action Context
			nodeList = doc.getElementsByTagName("action");
			for(int i=0; i<nodeList.getLength() ; i++)
			{
				Node node = nodeList.item(i);
				if (node.getNodeType() == Node.ELEMENT_NODE) 
				{
					Element element = (Element)node;
					ActionContext context = new ActionContext();
					context.setActionName(element.getAttribute("name"));
					context.setParent(contexts.get(element.getAttribute("parent")));
					context.setActionClassName(element.getAttribute("class"));
					context.setActionMethodName(element.getAttribute("method"));

					Map<String, ActionResult> results = new HashMap<String, ActionResult>();
					// 先置入全局result集
					for (int k=0; k<global_results.size(); k++) {
						results.put(global_results.get(k).getName(), global_results.get(k));
					}
					// 置入当前ActionContext配置的result集
					NodeList subNodeList = element.getElementsByTagName("result");
					for (int j=0; j<subNodeList.getLength(); j++) 
					{
						Node resultNode = subNodeList.item(j);
						if (resultNode.getNodeType() == Node.ELEMENT_NODE) 
						{
							Element resultElement = (Element)resultNode;
							ActionResult result = new ActionResult();
							result.setName(resultElement.getAttribute("name"));
							result.setType(Integer.parseInt(resultElement.getAttribute("type")));
							result.setValue(resultElement.getAttribute("value"));
							results.put(result.getName(), result);
						}
					}
					context.setResults(results);
					contexts.put(context.getActionName(), context);
					actions.add(context.getActionName());
				}
			}

			// 初始化模板列表
			nodeList = doc.getElementsByTagName("style");
			for(int i=0; i<nodeList.getLength() ; i++)
			{
				Node node = nodeList.item(i);
				if (node.getNodeType() == Node.ELEMENT_NODE) 
				{
					Element element = (Element)node;
					styles.add(element.getAttribute("name"));
					if (element.getAttribute("type").equals("admin"))
					{
						acp_style_index = styles.size() - 1;
					}
				}
			}

			// 初始化语系档
			nodeList = doc.getElementsByTagName("language");
			for(int i=0; i<nodeList.getLength() ; i++)
			{
				Node node = nodeList.item(i);
				if (node.getNodeType() == Node.ELEMENT_NODE) 
				{
					Element element = (Element)node;
					Language lang = new Language(
							i, 
							element.getAttribute("name"), 
							element.getAttribute("country"), 
							element.getAttribute("script"), 
							element.getAttribute("localname"));

					languages.add(lang);
				}
			}

			// 初始化导航条目, 此时不检查navigator是否有效, 但是除了根节点以外, 其他都必须是已经定义的action, 
			// 并且有对应的多国化资源
			nodeList = doc.getElementsByTagName("navigation");
			if (nodeList != null && nodeList.getLength() > 0)
			{
				for(int i=0; i<nodeList.getLength() ; i++)
				{
					Node node = nodeList.item(i);
					if (node.getNodeType() == Node.ELEMENT_NODE) 
					{
						Element element = (Element)node;
						String navName = element.getAttribute("name");
						String[] childrenNames = element.getAttribute("value").trim().split("\\s*,\\s*");

						ActionNavigator nav = navigations.get(navName);
						if (nav==null)
						{
							nav = new ActionNavigator(navName);
							navigations.put(navName, nav);
						}

						for (String childName: childrenNames)
						{
							if (navigations.get(childName) != null)
							{
								nav.getChildren().add(navigations.get(childName));
							}
							else
							{
								ActionNavigator childNav = new ActionNavigator(childName);
								childNav.setParent(nav);
								nav.getChildren().add(childNav);
								navigations.put(childName, childNav);
							}
						}

					}
				}
			}
		}
		catch (Exception e)
		{
			logger.info("Error occurs in parsing Simpleframe context file");
			logger.debug(e.getMessage(), e);
		}

		// 以下try..catch填充各个role对action的访问权限
		try 
		{
			InputStream is = SimpleConfig.class.getResourceAsStream("/simpleframe-acl.xml");
			DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
			DocumentBuilder db = dbf.newDocumentBuilder();
			InputSource isc = new InputSource(is);
			Document doc = db.parse(isc);

			// 初始化 ACL 角色
			NodeList nodeList = doc.getElementsByTagName("role");
			if (nodeList != null && nodeList.getLength() > 0)
			{
				for(int i=0; i<nodeList.getLength() ; i++)
				{
					Node node = nodeList.item(i);
					if (node.getNodeType() == Node.ELEMENT_NODE) 
					{
						Element element = (Element)node;
						String name = element.getAttribute("name"); 
						int index = Integer.parseInt(element.getAttribute("index"));
						int supervisor = Integer.parseInt(element.getAttribute("superuser"));

						// Avoid outOfBounce error
						if (index > ACLBuilder.ACL_SIZE * 6 - 1) continue;

						rolesMap.put(name, index);
						roles.add(name);
						if (supervisor > 0) {superusers.add(index);}
					}
				}
			}

			// 初始化 Accesses
			nodeList = doc.getElementsByTagName("acl");
			if (nodeList != null && nodeList.getLength() > 0)
			{
				for(int i=0; i<nodeList.getLength() ; i++)
				{
					Node node = nodeList.item(i);
					if (node.getNodeType() == Node.ELEMENT_NODE) 
					{
						Element element = (Element)node;
						String action = element.getAttribute("action"); 
						ActionContext context = SimpleConfig.getActionContext(action);
						if (context == null) continue;
						String[] allows = element.getAttribute("roles").trim().split("\\s*,\\s*");
						List<Integer> indexes = new ArrayList<Integer>();
						for (String allow : allows)
						{
							if (rolesMap.get(allow) == null) continue;
							indexes.add(rolesMap.get(allow));
						}
						context.setAccesses(indexes);
					}
				}
			}

			// 填充 Action Accesses, 如果子action的accesses为空, 会继承父action的accesses
			for (String action : SimpleConfig.getAllActionName())
			{
				ActionContext context = SimpleConfig.getActionContext(action);
				if (context.getAccesses() == null || context.getAccesses().size() == 0)
				{
					List<Integer> newAccesses = trackAccesses(context);
					if (newAccesses != null && newAccesses.size() > 0)
						context.setAccesses(newAccesses);
				}
			}
		}
		catch (Exception e)
		{
			logger.info("Error occurs in parsing ACL configuration file");
			logger.debug(e.getMessage(), e);
		}
	}

	private SimpleConfig() {}

	/**
	 * 向上追溯action context 得到最接近的 acl 设定
	 * 
	 * @param context
	 * @return
	 */
	private static List<Integer> trackAccesses(ActionContext context) 
	{
		List<Integer> accesses = context.getAccesses();
		if (accesses == null || accesses.size()==0)
		{
			if (context.getParent() == null)
				return accesses;
			else
				return trackAccesses(context.getParent());
		}
		else
			return accesses;
	}

	public static Map<String, String> getConfigs() {	return configs;}

	public static String getConfig(String con_name) {return configs.get(con_name);}

	public static ActionContext getActionContext(String act_name) {return contexts.get(act_name);}

	public static List<ActionContext> getActionPath(String act_name)
	{
		List<ActionContext> path = new ArrayList<ActionContext>();
		for (ActionContext act = contexts.get(act_name); act!=null; act = act.getParent())
		{
			path.add(0, act);
		}
		return path;
	}

	public static List<String> getAllActionName() {return actions;}

	public static Map<String, ActionNavigator> getNavigations() {return navigations;}
	
	/**
	 * Get a copy of the navigation, make it thread-safe for sessions to modify the 'current' information
	 * 
	 * @return
	 */
	public static Map<String, ActionNavigator> getCloneNavigators()
	{
		Map<String, ActionNavigator> navs = new HashMap<String, ActionNavigator>();
		Collection<ActionNavigator> src_navs = navigations.values();
		for (ActionNavigator src_nav: src_navs)
		{
			ActionNavigator nav = new ActionNavigator(src_nav.getName());
			navs.put(src_nav.getName(), nav);
		}
		for (ActionNavigator src_nav: src_navs)
		{
			ActionNavigator nav = navs.get(src_nav.getName());
			
			if (src_nav.getParent() != null)
			{
				nav.setParent(navs.get(src_nav.getParent().getName()));
			}

			for (ActionNavigator child: src_nav.getChildren())
			{
				nav.getChildren().add(navs.get(child.getName()));
			}
		}
		return navs;
	}

	public static ActionNavigator getNavigation(String nav_name) {return navigations.get(nav_name);}

	public static List<String> getStyles() {return styles;}
	public static int getAcpStyleIndex() {return acp_style_index;}

	public static List<Language> getLanguages() {return languages;}

	public static List<String> getRoles() {return roles;}
	public static Collection<Integer> getRoleIndexes() {return rolesMap.values();}

	public static Integer getDefaultRoleIndex() {return rolesMap.get(roles.get(0));}
	public static Integer getRoleIndex(String role) {return rolesMap.get(role);}
	public static List<Integer> getSuperUserIndexes() {return superusers;}

}
